package com.scu.qingshi.domain.interview.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.scu.qingshi.common.exception.interview.InterviewException;
import com.scu.qingshi.config.cummunity.CommunityConfig;
import com.scu.qingshi.config.cummunity.CommunityFactory;
import com.scu.qingshi.domain.announcement.enums.NoticeTypeEnum;
import com.scu.qingshi.domain.interview.event.NodeFailEvent;
import com.scu.qingshi.domain.interview.event.NodePassEvent;
import com.scu.qingshi.domain.interview.event.NoticeNextNodeEvent;
import com.scu.qingshi.domain.interview.param.*;
import com.scu.qingshi.infrastructure.dao.po.*;
import com.scu.qingshi.infrastructure.dao.service.*;
import com.scu.qingshi.infrastructure.event.EventBusCenter;
import com.scu.qingshi.infrastructure.utils.DateUtil;
import com.scu.qingshi.infrastructure.utils.EmailUtil;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;

/**
 * 面试流程的domain服务
 *
 * @author qingshi
 */
@Service
public class InterviewDomainService {

    private final Logger logger = LogManager.getLogger(this.getClass());

    @Autowired
    InterviewProcessService interviewProcessService;

    @Autowired
    NodeService nodeService;

    @Autowired
    InterUserMapService interUserMapService;

    @Autowired
    EventBusCenter eventBusCenter;

    @Autowired
    AnnouncementService announcementService;

    @Autowired
    UserService userService;

    @Autowired
    EmailUtil emailUtil;

    public void addOneNode(Node node) throws InterviewException {

        InterviewProcess process = interviewProcessService.getById(node.getInterId());

        // 进行日期、逻辑等校验
        validation(node, process, true);

        nodeService.save(node);

        process.addNewNode(node.getNodeId());
        logger.info("添加节点后的节点id流 => {}", process.getNodes());

        UpdateWrapper<InterviewProcess> wrapper = new UpdateWrapper<>();
        wrapper.eq("inter_id", process.getInterId());
        interviewProcessService.update(process, wrapper);

    }

    void validation(Node node, InterviewProcess process, boolean isAdd) throws InterviewException {
        // 防止节点开始时间在报名截止之前
        if (node.getStartTime().compareTo(process.getDeadline()) < 0) {
            throw InterviewException.dateError("节点开始时间不能在报名截止时间之前");
        }

        // 添加节点，直接校验最后一个节点
        if (isAdd) {
            // 防止新添加的节点开始时间在上一个节点结束时间之前
            Pair<Boolean, Integer> lastIdPair = process.getLastNodeId();

            if (lastIdPair.getLeft()) {
                Node preNode = nodeService.getById(lastIdPair.getRight());
                if (node.getStartTime().compareTo(preNode.getEndTime()) < 0) {
                    throw InterviewException.dateError("后置节点开始时间不能在前置节点的结束时间之前");
                }
            }

            if (process.hasComplete()) {
                throw InterviewException.publishError("面试节点已达到上限，不能添加新节点");
            }

            if (process.hasPublish()) {
                throw InterviewException.publishError("面试已发布，不能添加新节点");
            }
        }
        // 修改面试的校验规则，是判断前一个
        else {
            Pair<Boolean, Integer> preId = process.preNode(node.getNodeId());
            if (preId.getLeft()) {
                Node preNode = nodeService.getById(preId.getRight());
                if (node.getStartTime().compareTo(preNode.getEndTime()) < 0) {
                    throw InterviewException.dateError("后置节点开始时间不能在前置节点的结束时间之前");
                }
            }
        }

    }

    public void publish(PublishRequest request) throws InterviewException {

        InterviewProcess interviewProcess = interviewProcessService.getById(request.getInterId());

        if (interviewProcess.hasPublish()) {
            throw InterviewException.publishError("面试已发布，不能重复发布");
        }

        if (!interviewProcess.hasComplete()) {
            throw InterviewException.publishError("面试节点少于预期设定，不能发布");
        }

        if (DateUtil.expirationDate(interviewProcess.getDeadline())) {
            throw InterviewException.dateError("面试报名截止时间过期，请重新修改报名截止日期");
        }

        interviewProcess.publish();

        interviewProcessService.iUpdate(interviewProcess);

    }

    public String apply(ApplyRequest request) throws InterviewException {
        // todo 不同社团可以设置不同的申请过滤规则
        final int NO = 0;

        InterUserMap one = interUserMapService.getInterUserMap(request.getInterId(), request.getUserId());
        InterviewProcess process = interviewProcessService.getById(request.getInterId());
        if (Objects.isNull(one)) {

            if (DateUtil.expirationDate(process.getDeadline())) {
                throw InterviewException.dateError("抱歉，面试报名已截止");
            }


            InterUserMap map = InterUserMap.builder()
                    .interId(process.getInterId())
                    .userId(request.getUserId())
                    .nodeCount(process.getNodeCount())
                    // 此处申请时，面试流程的节点不会为null，所以这里不做判断
                    .curNodeId(process.getFirstNodeId().getRight())
                    .pass(NO)
                    .applyReason(request.getApplyReason())
                    .interName(process.getName())
                    .openId(process.getOpenId())
                    .build();

            interUserMapService.save(map);

            return process.getOpenId();
        } else if (one.canApply()) {
            one.setPass(NO);
            one.setApplyReason(request.getApplyReason());
            one.setCurNodeId(process.getFirstNodeId().getRight());
            interUserMapService.iUpdate(one);
            return one.getOpenId();
        } else {
            throw InterviewException.applyError("面试已通过或者您已申请但社团还未审批,不能重复申请");
        }
    }

    public void audit(AuditRequest req) throws InterviewException {
        InterUserMap map = interUserMapService.getInterUserMap(req.getInterId(), req.getUserId());

        if (Objects.isNull(map)) {
            throw InterviewException.auditError("申请不存在,请联系管理员排查");
        }

        if (map.getPass() != 0) {
            throw InterviewException.auditError("已审核，请不要重复审核");
        }

        map.audit(req.getAuditResult());

        interUserMapService.iUpdate(map);

    }

    public void record(RecordReviewRequest request) throws Exception {

        InterUserMap map = interUserMapService.getInterUserMap(request.getInterId(), request.getUserId());

        if (Objects.isNull(map)) {
            throw new Exception("所记录的面试流不存在，请联系管理员");
        }

        map.review(request.getReview());

        interUserMapService.iUpdate(map);
    }

    public boolean judge(JudgeRequest req) throws InterviewException {
        boolean isSuccess = false;
        Node node = nodeService.getById(req.getNodeId());

        if (DateUtil.expirationDate(node.getStartTime())) {
            throw InterviewException.judgeError("此轮面试还未开始，不能进行处理");
        }

        InterUserMap map = interUserMapService.getInterUserMap(req.getInterId(), req.getUserId());

        // 如果此面试已经被判决过，则不能再次判决，此处是一个幂等操作
        if (map.getSuccess() != 0) {
            throw InterviewException.judgeError("该面试流已被判决，不能重复判决");
        }

        // 此轮面试不通过
        if (!req.getSuccess()) {
            map.judge(req.getSuccess());
            // 同步邮件 通知面试失败
            eventBusCenter.postEvent(
                    NodeFailEvent.builder()
                            .nodeId(req.getNodeId())
                            .openId(req.getOpenId())
                            .userId(req.getUserId())
                            .build());
        } else {
            InterviewProcess process = interviewProcessService.getById(req.getInterId());
            // 代表是最后一个节点
            if (Objects.equals(process.getLastNodeId().getRight(), map.getCurNodeId())) {
                map.judge(req.getSuccess());
                // 同步邮件 通知面试通过
                eventBusCenter.postEvent(
                        NodePassEvent.builder()
                                .nodeId(req.getNodeId()).openId(req.getOpenId()).userId(req.getUserId())
                                .build());
                User user = userService.getById(req.getUserId());
                CommunityConfig community = CommunityFactory.getCommunity(req.getOpenId());
                emailUtil.sendEmail(community.getEmail(),
                        user.getEmail(),
                        "来自" + community.getName() + "的通知邮件",
                        "恭喜您通过本社团的所有面试，现诚邀您加入我们社团，请前往平台进行处理，一个月后过期");

                isSuccess = true;
                announcementService.save(Announcement.builder()
                        .userId(req.getUserId())
                        .openId(req.getOpenId())
                        .content("您已通过我们社团的所有面试，诚邀您加入社团")
                        .type(NoticeTypeEnum.INVITE_TO_JOIN.name())
                        .userEmail(user.getEmail())
                        // 过期时间一个月
                        .expireTime(cn.hutool.core.date.DateUtil.nextMonth())
                        .build());

            } else {
                // 推进面试到下一个节点
                Integer nextId = process.passCurNode(String.valueOf(map.getCurNodeId()));
                map.setCurNodeId(nextId);
                // 同步邮件 通知面试通过
                eventBusCenter.postEvent(
                        NodePassEvent
                                .builder()
                                .nodeId(req.getNodeId()).openId(req.getOpenId()).userId(req.getUserId())
                                .build());
                // 异步事件邮件 通知注意查看下一场面试
                eventBusCenter.postEvent(
                        NoticeNextNodeEvent
                                .builder()
                                .nodeId(nextId).openId(req.getOpenId()).userId(req.getUserId())
                                .build());
            }
        }
        interUserMapService.iUpdate(map);

        return isSuccess;
    }

//    public void delete(DeleteNodeRequest req) throws InterviewException {
//
//        Node node = nodeService.getById(req.getNodeId());
//
//        InterviewProcess process = interviewProcessService.getById(node.getInterId());
//
//        if (process.hasPublish()){
//            throw InterviewException.publishError("已发布的面试不能进行节点删除");
//        }
//
//        process.deleteNode(node.getNodeId());
//
//        interviewProcessService.iUpdate(process);
//        nodeService.removeById(node.getInterId());
//
//    }

    public void close(CloseInterviewRequest req) {

        InterviewProcess process = interviewProcessService.getById(req.getInterId());

        process.soldOut();

        interviewProcessService.iUpdate(process);
    }

    public void deleteNode(Integer interId, Integer nodeId) throws InterviewException {

        InterviewProcess process = interviewProcessService.getById(interId);
        if (process.hasPublish()) {
            throw InterviewException.deleteError("面试已发布，不能删除相关节点");
        }

        if (process.getLastNodeId().getLeft()) {
            if (!process.getLastNodeId().getRight().equals(nodeId)) {
                throw InterviewException.deleteError("必须先删除后置节点");
            }
        } else {
            throw InterviewException.deleteError("面试流程无节点，请联系管理员");
        }

        QueryWrapper<InterUserMap> wrapper = new QueryWrapper<>();
        // 有申请通过，且面试未结束
        wrapper.eq("inter_id", interId)
                .eq("pass", 1)
                .eq("success", 0);
        List<InterUserMap> maps = interUserMapService.getBaseMapper().selectList(wrapper);
        if (!maps.isEmpty()) {
            throw InterviewException.deleteError("有用户正在进行相关面试，不能删除");
        }

        process.deleteNode(nodeId);

        // 持久化
        interviewProcessService.iUpdate(process);

    }

    public String modifyNode(ModifyNodeRequest req) throws Exception {

        Node node = nodeService.getById(req.getNodeId());

        if (DateUtil.expirationDate(node.getStartTime())) {
            throw InterviewException.modifyError("面试进行中，不能修改");
        }

        // 防止面试节点截止时间在开始时间之前
        if (DateUtil.convertTextToDate(req.getEndTime())
                .compareTo(DateUtil.convertTextToDate(req.getStartTime())) <= 0) {
            throw InterviewException.dateError("节点的截止日期小于开始日期");
        }

        InterviewProcess process = interviewProcessService.getById(req.getInterId());


        node.setOnline(req.getIsOnline() ? 1 : 0);
        node.setInterPlace(req.getInterPlace());
        node.setInformWay(req.getInformWay());
        node.setInformPass(req.getInformPass());
        node.setInformFail(req.getInformFail());
        node.setInterContent(req.getInterContent());
        node.setEndTime(DateUtil.convertTextToDate(req.getEndTime()));
        node.setStartTime(DateUtil.convertTextToDate(req.getStartTime()));

        validation(node, process, false);

        nodeService.iUpdate(node);

        return process.getOpenId();

    }
}
